#include "aerial\Core\NetIO.h"
#include "aerial\Core\NetMessage.h"

#include <iostream>

using namespace air;
using namespace std;

#define SERVER_PORT	2012
#define SERVER_IP "127.0.0.1"

#include "raknet\RakString.h"

NetIO::NetIO(): server(false)
{
	node = RakPeerInterface::GetInstance();
	readyCount = 0;
	expectedPlayerCount = 0;
	me = 0;
}

void NetIO::startBattle()
{
	if (server) StartBattleServer();
	else ConnectToBattleServer();
}

void NetIO::reset() {
	expectedPlayerCount = 0;
	readyCount = 0;
	server = false;
	me = 0;
}

void NetIO::ConnectToMainServer()
{
	SocketDescriptor sd(0, 0);
	sd.socketFamily = AF_INET;

	node->Startup(4, &sd, 1);
	node->SetOccasionalPing(true);
	node->AllowConnectionResponseIPMigration(true);
	//TODO
#ifdef _DEBUG
	ConnectionAttemptResult car = node->Connect(SERVER_IP, SERVER_PORT, "", 0);
	RakAssert(car==CONNECTION_ATTEMPT_STARTED);
#endif
}

void NetIO::StartBattleServer()
{
	node->SetTimeoutTime(1000,UNASSIGNED_SYSTEM_ADDRESS);
	SocketDescriptor sd(SERVER_PORT, 0);
	sd.socketFamily = AF_INET;
#ifdef _DEBUG
	StartupResult sr = node->Startup(9, &sd, 1);
	RakAssert(sr == RAKNET_STARTED);
#endif
	node->SetMaximumIncomingConnections(9);
	node->SetOccasionalPing(true);
	node->SetUnreliableTimeout(1000);
	playerInfo[0].id = 0;
	playerInfo[0].ready = true;
	readyCount = 1;
}

void NetIO::ConnectToBattleServer()
{
	SocketDescriptor sd(2013, 0);
	sd.socketFamily = AF_INET;

	node->Startup(4, &sd, 1);
	node->SetOccasionalPing(true);
	//node->SetTimeoutTime(1000, playerInfo[0].sa);
	node->SetTimeoutTime(1000, UNASSIGNED_SYSTEM_ADDRESS);
	node->AllowConnectionResponseIPMigration(true);
	//TODO
#ifdef _DEBUG
	// playerInfo[0] means the player which hosts a battle
	ConnectionAttemptResult car = node->Connect(playerInfo[0].sa.ToString(), playerInfo[0].sa.GetPort(), "", 0);
	RakAssert(car==CONNECTION_ATTEMPT_STARTED);
#endif
}

int NetIO::findPlayerBySystemAddress(SystemAddress& sysAddr)
{
	for (int i = 0; i < expectedPlayerCount; i++) 
		if (sysAddr == playerInfo[i].sa) return playerInfo[i].id;
	return expectedPlayerCount;
}

bool NetIO::ReadyBattle()
{
	Packet* p;
	bool status = false;
	if (server)
	{
		if (p = node->Receive())
		{
			switch(p->data[0])
			{
			case ID_NEW_INCOMING_CONNECTION:
				{
				cout << "new conn\n";
				int id;
				if ((id = findPlayerBySystemAddress(p->systemAddress)) < expectedPlayerCount)
				{
					playerInfo[id].ready = true;
					++readyCount;
					if (readyCount == expectedPlayerCount) status = true;
				}
				else {
					cout << "can't find specified player with this ip addr.\n";
				}
				break;
				}
			case ID_CONNECTION_LOST:
				{
					cout << "connection lost.\n";
					int id;
					if ((id = findPlayerBySystemAddress(p->systemAddress)) < expectedPlayerCount)
					{
						playerInfo[id].ready = false;
						--readyCount;
					}
					else {
						cout << "can't find specified player with this ip addr.\n";
					}
					break;
				}
			default:
				cout << "server error: handle packet with head " << (int)p->data[0] << endl;
			}
			node->DeallocatePacket(p);
		}
		if (status) {
			ReadyAck readyAck;
			broadcastExcludePlayer(&readyAck, -1);
		}
	} else {
		if (p = node->Receive())
		{
			switch (p->data[0]) {
				case ID_CONNECTION_REQUEST_ACCEPTED:
					cout << "accepted\n";
					break;
				case ID_CONNECTION_LOST:
					cout << "connection lost from server\n";
					break;
				case READY_ACK:
					cout << "all ready\n";
					status = true;
					break;
				default:
					cout << "client error " << p->data[0] << endl;
			}
			node->DeallocatePacket(p);
		}
	}
	return status;
}

void NetIO::broadcastExcludePlayer(NetMessage* nMsg, int playerId)
{
	BitStream bso;
	nMsg->Serialization(&bso);
	if (playerId == -1) 
		node->Send(&bso, HIGH_PRIORITY, RELIABLE_ORDERED, 0, UNASSIGNED_SYSTEM_ADDRESS, true);
	else
		node->Send(&bso, HIGH_PRIORITY, RELIABLE_ORDERED, 0, playerInfo[playerId].sa, true);
}

void NetIO::sendToBattleServer(NetMessage* nMsg)
{
}

void NetIO::recv(Packet* p) {	p = node->Receive();		}

NetIO::~NetIO()
{
	node->Shutdown(300);
	RakPeerInterface::DestroyInstance(node);
}